import ComparableObj from "../core/ComparableObj";
import IllegalArgumentException from "../exceptions/IllegalArgumentException";

type Comparator = <E> (a: E, b: E) => returnType;

/**
 * 若前者大于后者，则返回1，若后者大于前者，则返回-1，否则返回0。
 *
 * @version 1.0
 */
type returnType = -1 | 0 | 1;

/**
 * 对两个对象进行比较。
 *
 * 将使用默认的比较器进行比较。
 *
 * @param a 第一个对象
 * @param b 第二个对象
 * @return {returnType} 比较结果
 * @version 1.0
 */
const numberComparator: Comparator = <E>(a: E, b: E) => {
  if (a < b) {
    return -1;
  } else if (a > b) {
    return 1;
  } else {
    return 0;
  }
}

/**
 * 对两个对象进行比较。
 *
 * 如果其中一个对象是undefined，则它始终是小的那一方。如果两个对象都是undefined，则两个相等。
 *
 * @param a 第一个元素
 * @param b 第二个元素
 * @return {returnType} 比较结果
 * @version 1.0
 */
const defaultComparator: Comparator = <E>(a: E | undefined, b: E | undefined) => {
  if (a === undefined && b === undefined) {
    return 0;
  } else if (a === undefined && b !== undefined) {
    return -1;
  } else if (a !== undefined && b === undefined) {
    return 1;
  } else {
    return numberComparator(a, b);
  }
}

/**
 * 对两个给定的对象进行比较以判断两者中谁更大。
 *
 * 如果你提供了一个比较器，比较器将用于比较。
 * 否则，它会检查E类是否继承自{@link ComparableObj}，并使用{@link ComparableObj#compare}进行比较；或者检查E类是否为String、Number或
 * Boolean，并使用{@link #defaultComparator}进行比较。
 *
 * 即按优先级排序，此方法的比较器>元素的compare方法（如果有的话）>元素的默认排序（基础类型的排序功能）
 *
 * 若以上的排序方法均不可用，则会抛出异常{@link IllegalArgumentException}，因为对object进行比较将永远得到错误答案
 *
 * @param a 第一个对象
 * @param b 第二个对象
 * @param comparator 比较器。这是最高的优先级
 * @return 比较结果
 * @throws {IllegalArgumentException} 没有给出比较器，且元素不支持排序
 * @version 1.0
 */
export function compareTo(a: any, b: any, comparator?: Comparator): returnType {
  if (comparator !== undefined) {
    return comparator(a, b);
  } else if (a instanceof ComparableObj && b instanceof ComparableObj) {
    return a.compareTo(b);
  } else if (["number", "string", "boolean"].indexOf(typeof a) !== -1) {
    return defaultComparator(a, b);
  } else {
    throw new IllegalArgumentException("you should either provide a comparator or make the class extends ComparableObj");
  }
}

export {defaultComparator, returnType};
export default Comparator;
